package alipay

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"errors"
	"net/url"
	"organoid_public/pkg/alipay/entity"
	"organoid_public/pkg/http"
	"strings"
	"time"
)

// 支付宝公共参数
const (
	TIME_FMT           = "2006-01-02 15:04:05"
	Appid              = "2021001166627971"
	SignType           = "RSA2"
	GatewayUrl         = "https://openapi.alipay.com/gateway.do"
	Format             = "json"
	PostCharset        = "UTF-8"
	Version            = "1.0"
	RsaPrivateKey      = "-----BEGIN zq test rsa-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCCJalEMEiYJ2Oefrd4DHh5kEFrR+4pMB/xVApHz7+F4awLRlIIdMWR3+6CMX41V2mLlhl2DV6jIIMevFGH14d/hONQeG///6Rpzd052hB786VwL0+NlmAWb+sD8q8WjP/bJdHupWQ/Y2Uj0opMD0C4laPTsu7GsNLABMe4ULQ/qshAiQGAzGow6tvC79+yAeQxTLGgS0SswOsBn0hO/pzurYobn+xtAQ3WTiaj5Kr/i537PliJxnPx3m1W3zNBNd31O895m9a3TOdRcd4/QNdxcuofM35jIv/SHJF/+DL2Pn7JLEOSu2uRBf8asFCzTUATk4T10ql42lBnyFpaq1dRAgMBAAECggEAXx/hyRo1ZSidEnJx7QfIYqnCdwQ7GdW38y7G2NgKl3NryU3blZyr70QqUvST168NUN4ju/pnqnmP/L4OJWit+9GIR//PdOBILqEuSiONmttpl0atMcKwdpXUw6z0tlnWhDnvaCtwyzf966HuPUuEtaWB+bC1/3msgHa994TdSg6hqNYUYMLBVIosx4SkTFf94tCPjHwpjV1MD8PERQcXJbff6x1f15tCm2DrhouVrZjL2gKIh2dAphLEt6t2YKDvbvDtbiBdVDiMaGX3ZBhhbA2uvjUpSgi/L1BIaHA+ZxuwTENcZ7wjr3+gGZXUplQ2KfooUfz1YtiFwoR52IrwAQKBgQC7CYWcinQxusCPB2FXoU9cfjXtOUyaI7orqRuFEmKX3ZgVKWg70ByjSDx5s/DoTOShabMUsgDeMSAQbARutpQJjZQym2BvHTXb6UcwNmciZfaj1nvXkumsda06xNyj7eLpCKgiHGt3w2Ldan5TzDNtBj03Xz33sG5KzQiOE5fYUQKBgQCyIkXw7+ol0ES0WcTSCVpJxoo9oinoOknseSHZOBzooAYau8Xki0g0dLqH7jWXU9MpIB5hrkTKgrm4Rt4MhjB3A1ABj5HPT7U6kjVxyY2XEkcOeTkooy52+F664rdu3PNNwMu906okNuHJHtb/vV91cHkkL0icNtDCgCNuTArPAQKBgA6TMGFBfMp/RhAfQBunC1smzqb8xxZf4FsUo3tcMmipMPe9MU0v63th3qyRNB22GD7n0Le1I1ItO9VrxXCnM6QG71x+NSaTfSX06MI+Cw1oKq8bwKzM2v7AcM5aluMDX6JPNWLrGgjMJgEhElUPXZaKXOzatCaADigwEBGaP0PxAoGBAJpAD2x5c7pWxIefjE2Mh8CGycJhIATWJJk+s07TxkSx/Vc87jnfWiECS1vVrFZj4eAkzeor66mo1YV66GDT+je0iJ0xY0Ovc/FKFjl4HMGKtDkWQ6aq11tx6yXNmKXJcm4L2uF/3j8Jvy5XJDnEFFsvj9cDks6isNcb+pJSCrIBAoGBALN5IZe0roTTbMCeQfubUEOJXdQxCqHjI0iwtQn1RUPx57orNybOmslWDztk6uFZrGJL3Y3ULxk/w3sDucfKyxXyaDTwWcCEelO2ktp9xk77HPZpSD67UlIBnpgfQHf4/v/1WwDJRyRse5dCl3BvyB5dguJWcApetaNpJMTQZthr\n-----END zq test rsa-----\n"
	AlipayRsaPublicKey = "-----BEGIN zq test rsa-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsSNLH8UjR9K8OOD6ro7qZM3quSFcNuGT6BONCdvL10HJSX7NsW1goGNGTYHGWw5uhicAxILUJoo4h8+cjBNUNsc1CoTIz48QaVoGyRFvjZIOhw4c0G4W6zFZ88Sv/Z86iS4os4rgIPsvzHfOeJwS6CBwDLp5zkFQXwSCMr7P4oSUSUGwno5RWl0oor3Rh0ZOR1l8AOchjiFRkic4kQUQTKmHixghIFi8RhCJHhGnt0a3rrC51PXG2h7f753rD5ZyrZSHOY3Xk0llDOtXRTLJjyYRtJVk/m7O+xPbgDUQ7JDgxnH6cls/Jczd5RgkCfuUkuRpmxMyaj31XRkM1aGtlwIDAQAB\n-----END zq test rsa-----\n"
	AppPublicKey       = "-----BEGIN zq test rsa-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgiWpRDBImCdjnn63eAx4eZBBa0fuKTAf8VQKR8+/heGsC0ZSCHTFkd/ugjF+NVdpi5YZdg1eoyCDHrxRh9eHf4TjUHhv//+kac3dOdoQe/OlcC9PjZZgFm/rA/KvFoz/2yXR7qVkP2NlI9KKTA9AuJWj07LuxrDSwATHuFC0P6rIQIkBgMxqMOrbwu/fsgHkMUyxoEtErMDrAZ9ITv6c7q2KG5/sbQEN1k4mo+Sq/4ud+z5YicZz8d5tVt8zQTXd9TvPeZvWt0znUXHeP0DXcXLqHzN+YyL/0hyRf/gy9j5+ySxDkrtrkQX/GrBQs01AE5OE9dKpeNpQZ8haWqtXUQIDAQAB\n-----END zq test rsa-----\n"
)

// 支付宝请求实体
type Aop struct {
	BusParams Values
}

// 支付宝请求实体初始化
func NewAop() *Aop {
	aop := Aop{Values{}}
	aop.BusParams.Set("app_id", Appid)
	aop.BusParams.Set("sign_type", SignType)
	aop.BusParams.Set("format", Format)
	aop.BusParams.Set("post_charset", PostCharset)
	aop.BusParams.Set("version", Version)
	aop.BusParams.Set("timestamp", time.Now().Format(TIME_FMT))
	return &aop
}

// 发起请求
func (a *Aop) Execute(entity entity.AlipayEntity, result interface{}) {
	a.BusParams.Set("method", entity.GetApiMethodName())
	a.BusParams.Set("biz_content", entity.GetBizContent())
	a.BusParams.Set("sign", a.Sign(a.BusParams.FormatParams()))
	httpClient := http.NewMyHttpClient(GatewayUrl+"?"+a.urlEncode(), nil)
	httpClient.SetHeader("content-type", "application/x-www-form-urlencoded;charset=utf-8")
	httpClient.SetParamsReader(a.urlEncode())
	httpRepBody := httpClient.Request(http.MYGET)
	var bizFieldName = strings.Replace(entity.GetApiMethodName(), ".", "_", -1) + "_response"

	repBody := make(map[string]json.RawMessage)
	err := json.Unmarshal([]byte(httpRepBody), &repBody)
	if err != nil {
		panic(err)
	}
	bizContentJson, ok := repBody[bizFieldName]
	if !ok {
		panic(errors.New("支付宝响应异常"))
	}
	var signBytes = repBody["sign"]
	if len(signBytes) > 1 {
		signBytes = signBytes[1 : len(signBytes)-1]
	}
	a.verSign(string(bizContentJson), signBytes)

	err = json.Unmarshal([]byte(bizContentJson), result)
	if err != nil {
		panic(err)
	}
}

// 签名
func (a *Aop) Sign(paramStrings string) string {
	privateKey := PrivateKeyDecode(RsaPrivateKey)
	h := sha256.New()
	h.Write([]byte(paramStrings))
	paramBySha256 := h.Sum(nil)
	signByteByPkcs, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, paramBySha256)
	if err != nil {
		panic(err)
	}
	return base64.StdEncoding.EncodeToString(signByteByPkcs)
}

func (a *Aop) urlEncode() string {
	buf := strings.Builder{}
	for key, item := range a.BusParams {
		if buf.Len() > 0 {
			buf.WriteString("&")
		}
		buf.WriteString(key)
		buf.WriteString("=")
		buf.WriteString(url.QueryEscape(item))
	}
	return buf.String()
}

func base64decode(data []byte) ([]byte, error) {
	var dBuf = make([]byte, base64.StdEncoding.DecodedLen(len(data)))
	n, err := base64.StdEncoding.Decode(dBuf, data)
	return dBuf[:n], err
}

// 签证签名
func (a *Aop) verSign(paramStrings string, sign []byte) {

	signByte, err := base64decode(sign)
	if err != nil {
		panic(err)
	}
	publicKey := PublicKeyDecode(AlipayRsaPublicKey)
	h := sha256.New()
	h.Write([]byte(paramStrings))
	paramBySha256 := h.Sum(nil)
	err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, paramBySha256, signByte)
	if err != nil {
		panic(err)
	}
}
